package codename.carrot.bomberman.network.server;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

import android.util.Log;
import codename.carrot.bomberman.network.MessageHandler;
import codename.carrot.bomberman.network.NetworkConnection;
import codename.carrot.bomberman.network.NetworkConnection.NetworkState;
import codename.carrot.bomberman.network.client.ClientConnectionThread;

/**
 * 
 * Sets up a connection as a server, accepting client to connect.
 * 
 * @author Group A4.
 * 
 */
public class Server extends Thread {

	/**
	 * String for debugging.
	 */
	private final String DEBUG = this.getClass().getSimpleName();
	/**
	 * The server socket used for accepting client connections.
	 */
	private ServerSocket serverSocket;
	/**
	 * The server connection for adding and mainting the connection.
	 */
	private ServerConnection serverConnection;
	/**
	 * The current accepting state of the server. (True if accepting clients).
	 */
	private boolean connect = false;
	/**
	 * The message handler.
	 */
	private MessageHandler messageHandle;

	/**
	 * Constructs a server.
	 * @param serverConnection The parent class that maintains the connections made by this class.
	 * @param messageHandler The message handler.
	 */
	public Server(ServerConnection serverConnection, MessageHandler messageHandler) {
		this.serverConnection = serverConnection;
		this.messageHandle = messageHandler;
		
		//The next two lines are to ensure that the client running on the server is set up.
		ServerClientConnectionThread scc = new ServerClientConnectionThread(serverConnection.getNumberOfConnections(), messageHandle);
		serverConnection.addConnection(scc);
		
		try {
			serverSocket = new ServerSocket(NetworkConnection.PORT);
			connect = true;
		} catch (IOException e) {
			serverConnection.setState(NetworkState.ERROR);
			serverConnection.terminate();
			Log.e(DEBUG, "Failed to create socket.", e);
		}
	}

	/**
	 * Terminate the server, closing the server socket and interrupting the thread.
	 */
	public void terminate() {
		try {
			connect = false;
			if (serverSocket != null)
				serverSocket.close();
			serverSocket = null;
			interrupt();
			System.out.println("SERVER IS CLOSED");
		} catch (Exception e) {
			Log.e(DEBUG, "Failed to terminate server!", e);
		}
	}

	/**
	 * Canceling accept on the server. No more clients will be able to connect after this is called.
	 */
	public void cancelAccept() {
		connect = false;
	}

	@Override
	public void run() {
		while (connect) {
			Socket socket;
			try {
				socket = serverSocket.accept();

				if (socket != null) {
					serverConnection.setState(NetworkState.CONNECTING);
					ClientConnectionThread ct = new ClientConnectionThread(
							socket, serverConnection.getNumberOfConnections(),
							serverConnection, messageHandle);
					synchronized (serverConnection) {
						serverConnection.addConnection(ct);
					}
				}
			} catch (Exception e) {
				serverConnection.terminate();
				Log.e(DEBUG, "Socket accept() failed.", e);
			}
		}
		terminate();
	}

}
